package com.longma.server.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.longma.server.config.ContentType;
import com.longma.server.constant.enums.ResponseCode;
import com.longma.server.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public abstract class AbstrctFileService  {
    /*
     * 所有文件保存的根路径
     */
    @Value("${upload.rootPath:./uploads}")
    protected String rootPath;

    /**
     * @author: zy
     * @description: 抽象方法 获取上传或者下载的文件的基路径
     * @date: 2020/5/7 11:29
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return ：返回文件基路径
     */
    protected String getFilePathPrefix(String... args) {
        String path;
        if (rootPath.startsWith("./")) {
            path = System.getProperty("user.dir") + File.separator + rootPath.replace("./","");
        } else {
            path = rootPath;
        }
        StringBuilder sb = new StringBuilder(path);
        for (String arg : args) {
            sb.append(File.separator).append(arg);
        }
        path = sb.toString();
        File file = new File(path);
        if(!file.exists()){
            file.mkdirs();
        }
        return path;
    }


    /**
     * 正则替换除了.之外的所有特殊字符
     * @param orgStr
     * @return
     */
    private static String replaceSpecStr(String orgStr){
        if (StrUtil.isNotBlank(orgStr) && !"".equals(orgStr.trim())) {
            String regEx="[\\s~·`!！@#￥$%^…&*（()）\\-—\\_=+【\\[\\]】｛{}｝\\|、\\\\；;：:‘'“”\"，,《<。》>/？?]";
            Pattern p = Pattern.compile(regEx);
            Matcher m = p.matcher(orgStr);
            return m.replaceAll("_");
        }
        return "";
    }

    /**
     * @author: zy
     * @description: 用于自定义上传文件保存到服务器的名称
     * 不适用原名称是因为源文件名称可能存在特殊字符导致一些异常
     * 【原创】http请求中加号被替换为空格？源码背后的秘密：https://www.cnblogs.com/thisiswhy/p/12119126.html
     * @date: 2020/5/7 11:29
     * @return ：返回保存文件名
     */
    protected String getSavedFileName(String dir, String originalFilename) {
        //移除文件中的空格
        originalFilename = replaceSpecStr(originalFilename);

        // 检测文件名重复
        File f = new File(dir + File.separator + originalFilename);
        if (f.exists()) {
            int splitIndex = originalFilename.lastIndexOf('.');
            StringBuilder sb = new StringBuilder(originalFilename);
            //没有找到.
            if(splitIndex == -1){
                sb.append("_" + System.currentTimeMillis());
            }else {
                sb.insert(splitIndex, "_" + System.currentTimeMillis());
            }
            return sb.toString();
        } else {
            return originalFilename;
        }
    }

    /**
     * @author: zy
     * @description: 删除目录   目录不存在也默认删除成功
     * @date: 2021/1/21 9:47
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return Boolean：
     */
    public Boolean deleteDir(String ...args){
        //获取保存文件的基路径
        String dir = getFilePathPrefix(args);

        //删除文件
        if(!FileUtil.del(dir)){
            log.error(String.format("dir %s delete fail",dir));
            return false;
        }
        return true;
    }


    /**
     * @author: zy
     * @description: 删除基路径下的文件  文件不存在也默认删除成功
     * @date: 2020/5/7 11:29
     * @param fileName：待删除的文件名
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return Boolean：
     */
    public Boolean deleteFile(String fileName,String ...args){
        //获取保存文件的基路径
        String pathPrefix = getFilePathPrefix(args);

        //删除文件
        if(!FileUtil.del(pathPrefix + File.separator + fileName)){
            log.error(String.format("file %s delete fail",fileName));
            return false;
        }
        return true;
    }

    /**
     * @author: zy
     * @description: 删除基路径下的文件列表
     * @date: 2020/5/7 11:29
     * @param fileNames：待删除的文件列表
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return ：
     */
    public void deleteFile(List<String> fileNames,String ...args){
        for(String fileName:fileNames){
            deleteFile(fileName,args);
        }
    }

    /**
     * @author: ljf
     * @description: 上传文件
     * @date: 2020/5/7 11:29
     * @param file:上传文件
     * @return ResponseEntity<String>:
     */
    public String uploadFile(MultipartFile file) {
        return this.uploadFile(file, "");
    }

    /**
     * @author: zy
     * @description: 上传文件
     * @date: 2020/5/7 11:29
     * @param file:上传文件
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return String:
     */
    public String uploadFile(MultipartFile file,String ...args){
        //获取保存文件的基路径
        String pathPrefix = getFilePathPrefix(args);

        //获取新的文件名
        String fileName = getSavedFileName(pathPrefix,file.getOriginalFilename());

        //创建文件对象 保存文件
        File newFile = new File(pathPrefix + File.separator + fileName);
        try {
            file.transferTo(newFile);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            if (e.getMessage().contains("File name too long") || e.getMessage().contains("文件名过长")) {
                throw new BusinessException(ResponseCode.FILE_NAME_TOO_LONG);
            } else if (e.getMessage().contains("文件名、目录名或卷标语法不正确")) {
                throw new BusinessException(ResponseCode.FILE_INCORRECT_GRAMMAR);
            } else {
                throw new BusinessException(ResponseCode.FILE_UPLOAD_FAILED);
            }
        }
        return fileName;
    }


    /**
     * @author: zy
     * @description: 从基路径中下载文件
     * @date: 2020/5/7 11:29
     * @param response:
     * @param fileName: 文件名
     * @param args:路径参数  如：/aaa/bbb/ccc
     * @return void:
     */
    public void downloadFile(HttpServletResponse response,String fileName,String ...args){
        //获取保存文件的基路径
        String pathPrefix = getFilePathPrefix(args);

        //创建文件对象 下载文件
        File f = new File(pathPrefix + File.separator + fileName);
        if (!f.exists()) {
            log.error(String.format("request file %s does not exist",fileName));
            response.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        }

        try (BufferedInputStream br = new BufferedInputStream(new FileInputStream(f));
             OutputStream out = response.getOutputStream()) {
            byte[] buf = new byte[1024];
            int len = 0;
            response.reset();
            response.setContentType(ContentType.getContentType(fileName));
            response.setHeader("Content-Disposition", "attachment; filename=" + URLUtil.encode(fileName));
            while ((len = br.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
    }

    /**
     * @Author lty
     * @Description 下载resource下的文件，比如组织模板文件
     * @Date 8:53 2020/4/25 0025
     * @Param [fileName, response]
     * @return void
     **/
    public void downloadResource(HttpServletResponse response, String fileName){
        ClassPathResource classPathResource = new ClassPathResource(fileName);
        try (InputStream inputStream = classPathResource.getInputStream();BufferedInputStream br = new BufferedInputStream(inputStream);
             OutputStream out = response.getOutputStream()) {
            byte[] buf = new byte[1024];
            int len = 0;
            response.reset();
            response.setContentType(ContentType.getContentType(fileName));
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            while ((len = br.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
    }
}
